package edu.berkeley.cs160.innovationers.shoppersidekick.server;

import java.util.HashSet;
import java.util.Iterator;
import java.util.TreeSet;

import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;

import edu.berkeley.cs160.innovationers.shoppersidekick.db.*;
import edu.berkeley.cs160.innovationers.shoppersidekick.lib.*;
import edu.berkeley.cs160.innovationers.shoppersidekick.rpclib.*;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

/**
 * This class serves as the RPC class. All methods in this class
 * pertain to remote procedures that can be called by an authorized client
 * such as the Android application ShopperSidekick
 * 
 * @author Nadir Muzaffar
 *
 */
@SuppressWarnings("serial")
public final class ShopperSidekickRPCServlet extends BaseRPCServlet{
	
	@RPCMethod(method = ShopperSidekickProtocol.Sync.METHOD)
	public JSONObject sync(final CallContext context) throws JSONException {
		JSONObject resp = new JSONObject();
		JSONObject methodParams = (JSONObject) context.getParams().get("params");
		
		String userName = methodParams.getString("user");
		
		PersistenceManager pm = context.getPersistenceManager();
		
		JSONArray modListNames = new JSONArray();
		JSONArray modListDates = new JSONArray();
		JSONArray modListItems = new JSONArray();
		
		UserModel user;
		try {
			user = pm.getObjectById(UserModel.class, userName);
		} catch (JDOObjectNotFoundException e) {
			e.printStackTrace();
			System.err.println("Sync: list not found, looking for a list that dosent belong to user or a user that doesnt exist");
			resp.put("success", "Sync: failed - user does not exist");
			return resp;
		}

		HashSet<Key> listKeys;
		try{
			listKeys = user.getUserLists();
		} catch (JDOObjectNotFoundException e) {
			resp.put("numOfLists", 0);
			resp.put("listNames", "[]");
			resp.put("listItems", "[]");
			resp.put("modListDates", "[]");
			resp.put("success", "true");
			return resp;
		}


		Iterator<Key> itr = listKeys.iterator();
		int numOfLists = 0;
		while(itr.hasNext()) {
			JSONObject jsonListItems = new JSONObject();
			Key key = itr.next();
			
			try {
				ListModel list = pm.getObjectById(ListModel.class, key);
				TreeSet<ItemModel> items = list.getListItems();
	
				JSONArray modItems = new JSONArray();
				JSONArray itemQuantities = new JSONArray();
				JSONArray itemCheckedVals = new JSONArray();
	
				Iterator<ItemModel> itemsItr = items.iterator();
				int numOfItems = 0;
				while(itemsItr.hasNext()) {
					ItemModel item = itemsItr.next();
	
					modItems.put(numOfItems, item.getItemName());
					itemQuantities.put(numOfItems, item.getQuantity());
					itemCheckedVals.put(numOfItems, "0");
	
					numOfItems++;
				}

				jsonListItems.put("numOfItems", numOfItems);
				jsonListItems.put("itemNames", modItems);
				jsonListItems.put("quantities", itemQuantities);
				jsonListItems.put("checkedVals", itemCheckedVals);
				
				modListNames.put(numOfLists, list.getListName());
				modListItems.put(numOfLists, jsonListItems);
				modListDates.put(numOfLists, "0");
				
			} catch (JDOObjectNotFoundException e) {
				modListNames.put(numOfLists, "null");
				modListItems.put(numOfLists, "[]");
				modListDates.put(numOfLists, "0");
			}

			numOfLists++;
		}

		resp.put("numOfLists", numOfLists);
		resp.put("listNames", modListNames);
		resp.put("listItems", modListItems);
		resp.put("modListDates", modListDates);

		resp.put("success", "true");

		return resp;
	}
	
	@RPCMethod(method = ShopperSidekickProtocol.AddListItem.METHOD)
	public JSONObject addListItem(final CallContext context) throws JSONException {
		JSONObject resp = new JSONObject();
		JSONObject methodParams = (JSONObject) context.getParams().get("params");
		
		String itemName = methodParams.getString(ShopperSidekickProtocol.AddListItem.PARAMETERS[0]);
		String listName = methodParams.getString(ShopperSidekickProtocol.AddListItem.PARAMETERS[1]);
		String userName = methodParams.getString(ShopperSidekickProtocol.AddListItem.PARAMETERS[2]);
		int quantity = methodParams.getInt(ShopperSidekickProtocol.AddListItem.PARAMETERS[3]);
		
		PersistenceManager pm = context.getPersistenceManager();
		try {
			UserModel user = pm.getObjectById(UserModel.class, userName);
			HashSet<Key> listKeys = user.getUserLists();
			
			Key listKey = KeyFactory.createKey(ListModel.class.getSimpleName(), listName );
			if( listKeys.contains(listKey) ) {
				ListModel list = (ListModel) pm.getObjectById(ListModel.class, listKey);
				
				list.addListItemToList( new ItemModel(itemName, quantity) );
				
				System.err.println("Nadir: item added to list!");
				
				pm.makePersistent(list);
			
				resp.put(ShopperSidekickProtocol.AddListItem.RETURN[0], "true: user added to list");
			} else {
				System.err.println("Nadir: item NOT added to list!");
				resp.put(ShopperSidekickProtocol.AddListItem.RETURN[0], "false: user or list not found");
			}
		} catch (JDOObjectNotFoundException e) {
			e.printStackTrace();
			System.err.println("Nadir: The user or list was not found!");
			resp.put(ShopperSidekickProtocol.AddListItem.RETURN[0], "false: user or list not found");
        }
		
		return resp;
	}
	
	@RPCMethod(method = ShopperSidekickProtocol.AddUserToList.METHOD)
	public JSONObject addUserToList(final CallContext context) throws JSONException {
		JSONObject resp = new JSONObject();
		
		JSONObject methodParams = (JSONObject) context.getParams().get("params");
		
		String userName = methodParams.getString(ShopperSidekickProtocol.AddUserToList.PARAMETERS[0]);
		String listName = methodParams.getString(ShopperSidekickProtocol.AddUserToList.PARAMETERS[1]);
		
		PersistenceManager pm = context.getPersistenceManager();
		try {
			UserModel user = pm.getObjectById(UserModel.class, userName);
			ListModel list = new ListModel(listName);
			
			list.addUserToList(user); //adds list to user as well
			
			System.err.println("Nadir: user added to list!");
			
			pm.makePersistent(list);
			pm.makePersistent(user);
			
			resp.put(ShopperSidekickProtocol.AddUserToList.RETURN[0], "true: user added to list");
		} catch (JDOObjectNotFoundException e) {
			e.printStackTrace();
			System.err.println("Nadir: The user or list was not found!");
			resp.put(ShopperSidekickProtocol.AddUserToList.RETURN[0], "false: user or list not found");
        }

		
		return resp;
	}

	@RPCMethod(method = ShopperSidekickProtocol.RegisterDeviceWithUser.METHOD)
	public JSONObject registerDeviceWithUser(final CallContext context) throws JSONException {
		JSONObject resp = new JSONObject();
		
		JSONObject methodParams = (JSONObject) context.getParams().get("params");
		if(methodParams == null)
			System.err.println("Nadir: methodParams is null ");
		
		String userName = methodParams.getString(ShopperSidekickProtocol.RegisterDeviceWithUser.PARAMETERS[1]);
		String deviceID = methodParams.getString(ShopperSidekickProtocol.RegisterDeviceWithUser.PARAMETERS[0]);
		
		PersistenceManager pm = context.getPersistenceManager();
		try {
			System.err.println("Nadir: checking if user exits..");
			UserModel user = pm.getObjectById(UserModel.class, userName);
			resp.put(ShopperSidekickProtocol.RegisterDeviceWithUser.RETURN[0], "false: user already exists");
			
		} catch (JDOObjectNotFoundException e) {
			System.err.println("Nadir: user not found - GOOD THING");
			
			UserModel newUser = new UserModel(userName, userName);
			newUser.registerDeviceToUser( new RegistrationModel(deviceID) );
			pm.makePersistent(newUser);
			
			resp.put(ShopperSidekickProtocol.RegisterDeviceWithUser.RETURN[0], "true: user registered");
        }
		
		return resp;
	}
	
	@RPCMethod(method = ShopperSidekickProtocol.CreateList.METHOD)
	public JSONObject createList(final CallContext context) throws JSONException {
		System.err.println("Nadir: createList() called");
		JSONObject resp = new JSONObject();
		
		JSONObject methodParams = (JSONObject) context.getParams().get("params");
		if(methodParams == null)
			System.err.println("Nadir: methodParams is null ");
		
		String listName = methodParams.getString(ShopperSidekickProtocol.CreateList.PARAMETERS[0]);
		String listCreatorEmail = methodParams.getString(ShopperSidekickProtocol.CreateList.PARAMETERS[1]);
		
		PersistenceManager pm = context.getPersistenceManager();
		try {
			UserModel listCreator = pm.getObjectById(UserModel.class, listCreatorEmail);
			
			ListModel list = new ListModel(listName);
			list.addUserToList(listCreator); //adds list to user as well
			
			System.err.println("Nadir: list created!");
			
			pm.makePersistent(list);
			pm.makePersistent(listCreator);
			
			resp.put(ShopperSidekickProtocol.CreateList.RETURN[0], "true");
		} catch (JDOObjectNotFoundException e) {
			e.printStackTrace();
			System.err.println("Nadir: The user was not found!");
			resp.put(ShopperSidekickProtocol.CreateList.RETURN[0], "false");
        }

		return resp;
	}
	
	/**
	 * This is where we take care of the C2DM messaging.
	 * 
	 * @param pm
	 * @param user
	 */
	public void enqueuePushNotification(PersistenceManager pm, UserModel user) {
		
	}
	
	/**
	 * Put all testing related methods here.
	 * 
	 */
	@RPCMethod(method = ShopperSidekickProtocol.Ping.METHOD)
	public JSONObject ping(final CallContext context) throws JSONException{
		JSONObject resp = new JSONObject();
		
		System.err.println("ping called");
		
		resp.put("return", "Version 45343434...");
			
		return resp;
	}
}
/*
 * for(int i = 0; i < numOfLists; i++) {
				String listName = listNames.getString(i);
				int dateModified = modDates.getInt(i);
				ListModel list = (ListModel) pm.getObjectById(ListModel.class, listName);
				
				if( dateModified < list.getModDate() ) {
					modLists.put(numOfModLists, listName);
					modItems.put(numOfModLists, "null" ); 
					numOfModLists++;
				}
			}
			
			while(numOfLists < listKeys.size()) {
				
			}
 */
/**
 * JSONRPCMethod format: to help visualize. JSONObject.put() takes care
 * of the format for you so you dont actually have to worry about this.
 * 
 * {
 * 	"method" : METHOD_NAME,
 *  "params" : {
 *  			PARAM_KEY_1 : "value1",
 *  			PARAM_KEY_2 : "value2",
 * 				PARAM_KEY_3 : "value3"
 * 				},
 * }
 */
